package com.gitee.xmhzzz.component.mongodb.core;

import com.gitee.xmhzzz.component.common.enums.PageResult;
import com.gitee.xmhzzz.component.common.enums.Pager;
import com.gitee.xmhzzz.component.common.exception.DefaultBusinessException;
import com.mongodb.BasicDBObject;
import com.mongodb.client.DistinctIterable;
import com.mongodb.client.MongoCursor;
import org.apache.commons.lang3.StringUtils;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.List;

/**
 * @ClassName MongoRepository
 * @Description
 * @Author wzq
 * @Date 2023/6/16 9:06
 * @Version 1.0
 */
public abstract class MongoRepository<T> extends AbstractMongoRepository {

    @Autowired
    @Qualifier("mongoTemplate")
    private MongoTemplate mongoTemplate;

    public MongoTemplate getMongoTemplate()
    {
        return mongoTemplate;
    }

    /**
     * 保存
     *
     * @author：yinls
     * @param entity
     */
    @Override
    public <T> void save(T entity)
    {
        getMongoTemplate().insert(entity);
    }

    /**
     * 主键删除
     *
     * @author：yinls
     * @param entityClass
     * @param id
     */
    @Override
    public <T> void delete(Class<T> entityClass, Serializable id)
    {
        Query query = new Query(Criteria.where("_id").is(id));
        getMongoTemplate().remove(query, entityClass);
    }


    /**
     * 主键查询
     *
     * @param entityClass
     * @param id
     * @return
     */
    @Override
    public <T> T findById(Class<T> entityClass, Serializable id)
    {
        return (T) getMongoTemplate().findById(id, entityClass);
    }

    /**
     * 查询一条记录
     *
     * @param entityClass
     * @param query
     * @return
     */
    @Override
    public <T> T findOne(Class<T> entityClass, Query query)
    {
        return (T) getMongoTemplate().findOne(query, entityClass);
    }

    /**
     * 查询唯一记录
     *
     * @param entityClass
     * @param query
     * @return
     */
    @Override
    public <T> T findUniqueResult(Class<T> entityClass, Query query)
    {
        List<T> list = getMongoTemplate().find(query, entityClass);
        if(CollectionUtils.isEmpty(list)) return null;
        if(list.size() > 1) throw new DefaultBusinessException("数据库记录大于1，count=" + list.size());

        return list.get(0);
    }

    /**
     * 获取符合条件的记录数
     *
     * @param entityClass
     * @param query
     * @return
     */
    @Override
    public <T> int getCount(Class<T> entityClass, Query query)
    {
        return ((Long) getMongoTemplate().count(query, entityClass)).intValue();
    }

    /**
     * 查询List
     *
     * @param entityClass
     * @param query
     * @return
     */
    @Override
    public <T> List<T> searchList(Class<T> entityClass, Query query)
    {
        return getMongoTemplate().find(query, entityClass);
    }

    /**
     * 分页排序查询
     *
     * @param entityClass
     * @param query
     * @param sortStr
     * @param pager
     * @return
     */
    @Override
    public <T> PageResult<T> searchPage(Class<T> entityClass, Query query, String sortStr, Pager pager)
    {
        int count = getCount(entityClass, query);

        if(pager == null) pager = new Pager(0, 100);
        pager.setTotalRow(count);
        int pageSize = pager.getRowsPerPage();
        int totalPageNum = pager.getTotalPage();
        int nowPage = pager.getCurrentPage();
        int start = (nowPage - 1) * pageSize;

        if(StringUtils.isNotEmpty(sortStr))
        {
            Sort sort = Sort.by(Direction.DESC, sortStr);
            query.with(sort);
        }
        query.skip(start).limit(pageSize);
        List<T> list = getMongoTemplate().find(query, entityClass);
        PageResult<T> pageInfo = new PageResult<>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setNowPage(nowPage);
        pageInfo.setTotalCount(count);
        pageInfo.setTotalPageNum(totalPageNum);
        pageInfo.setList(list);
        return pageInfo;
    }

    /**
     * 聚合查询：DemoTestRepositoryTest.aggregate
     * @param aggregation
     * @param collectionName
     * @param outputClass
     * @return
     */
    @Override
    public <T> List<T> aggregate(Aggregation aggregation, String collectionName, Class<T> outputClass)
    {
        return getMongoTemplate().aggregate(aggregation, collectionName, outputClass).getMappedResults();
    }

    /**
     * 去重统计
     *
     * @param collectionName 集合名
     * @param distinctFileName 去重字段名
     * @param filter 过滤条件
     * @param
     * @return
     */
    public <T> Integer distinctCount(String collectionName, String distinctFileName, Bson filter, Class<T> distinctClass)
    {
        DistinctIterable<T> dist = getMongoTemplate().getCollection(collectionName).distinct(distinctFileName, filter,
                distinctClass);
        MongoCursor<T> iterator = dist.iterator();
        Integer count = 0;
        while (iterator.hasNext())
        {
            iterator.next();
            count++;
        }
        return count;
    }

    /**
     * 分页排序查询
     *
     * @author：Hebojin
     * @param entityClass
     * @param query
     * @param sort
     * @param pager
     * @return
     */
    @Override
    public <T> PageResult<T> searchPageWithSort(Class<T> entityClass, Query query, Sort sort, Pager pager)
    {
        int count = getCount(entityClass, query);

        if(pager == null) pager = new Pager(0, 100);
        pager.setTotalRow(count);
        int pageSize = pager.getRowsPerPage();
        int totalPageNum = pager.getTotalPage();
        int nowPage = pager.getCurrentPage();
        int start = (nowPage - 1) * pageSize;
        if(sort != null)
        {
            query.with(sort);
        }
        query.skip(start).limit(pageSize);
        List<T> list = getMongoTemplate().find(query, entityClass);

        PageResult<T> pageInfo = new PageResult<>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setNowPage(nowPage);
        pageInfo.setTotalCount(count);
        pageInfo.setTotalPageNum(totalPageNum);
        pageInfo.setList(list);
        return pageInfo;
    }

    /**
     * 分页聚合查询：DemoTestRepositoryTest.searchWithAggregateTest
     * @param collectionName
     * @param outputClass
     * @param operations
     * @param sort
     * @param pager
     * @return
     */
    @SuppressWarnings("deprecation")
    @Override
    public <T> PageResult<T> searchWithAggregate(String collectionName, Class<T> outputClass, List<AggregationOperation> operations, Sort sort, Pager pager)
    {
        //总数查询
        Aggregation countAggregation = Aggregation.newAggregation(operations);
        int count = getMongoTemplate().aggregate(countAggregation, collectionName, BasicDBObject.class).getMappedResults().size();

        if(pager == null) pager = new Pager(0, 100);
        pager.setTotalRow(count);
        int pageSize = pager.getRowsPerPage();
        int totalPageNum = pager.getTotalPage();
        int nowPage = pager.getCurrentPage();
        int start = (nowPage - 1) * pageSize;
        //处理参数：注意排序、分页顺序
        if(sort != null)
        {
            operations.add(Aggregation.sort(sort));
        }
        operations.add(Aggregation.skip(start));
        operations.add(Aggregation.limit(pageSize));

        Aggregation dataAggregation = Aggregation.newAggregation(operations);
        List<T> list = getMongoTemplate().aggregate(dataAggregation, collectionName, outputClass).getMappedResults();

        PageResult<T> pageInfo = new PageResult<>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setNowPage(nowPage);
        pageInfo.setTotalCount(count);
        pageInfo.setTotalPageNum(totalPageNum);
        pageInfo.setList(list);
        return pageInfo;
    }
}
